home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 076-100 / disk_095 / journal / playback.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  19KB  |  659 lines

  1. /*
  2.  *  PLAYBACK.C  -  Plays back mouse and keyboard events that were recorded
  3.  *                 by the JOURNAL program.
  4.  *
  5.  *             Copyright (c) 1987 by Davide P. Cervone
  6.  *  You may use this code provided this copyright notice is kept intact.
  7.  */
  8.  
  9. #include "journal.h"
  10.  
  11. /*
  12.  *  Version number and author
  13.  */
  14. char *version = "Playback v1.0 (June 1987)";
  15. char *author  = "Copyright (c) 1987 by Davide P. Cervone";
  16.  
  17. /*
  18.  *  Usage string
  19.  */
  20. #define USAGE   "PLAYPACK [FROM] file [EVENTS n] [[NO]SMOOTH"
  21.  
  22. /*
  23.  *  Macros to tell whether the user pressed CTRL-C
  24.  */
  25. #define CONTROL       IEQUALIFIER_CONTROL
  26. #define KEY_C         0x33
  27. #define CTRL_C(e)     (((e)->ie_Qualifier & CONTROL) && ((e)->ie_Code == KEY_C))
  28.  
  29. /*
  30.  *  The packed code for a RAWMOUSE event with NOBUTTON pressed (i.e., one
  31.  *  that probably contains more than one event compressed into a single 
  32.  *  entry in the file).
  33.  */
  34. #define MOUSEMOVE     0xE2
  35.  
  36. /*
  37.  *  Macro to check whether a command-line argument matches a given string
  38.  */
  39. #define ARGMATCH(s)   (stricmp(s,*argv) == 0)
  40.  
  41. /*
  42.  *  The functions that PLAYBACK can perform
  43.  */
  44. #define SHOW_USAGE    0
  45. #define READ_JOURNAL  1
  46. #define JUST_EXIT     2
  47.  
  48.  
  49. /*
  50.  *  Global Variables
  51.  */
  52.  
  53. struct MsgPort *InputPort = NULL;       /* Port for the Input.Device */
  54. struct IOStdReq *InputBlock = NULL;     /* Request block for the Input.Device */
  55. struct Task *theTask = NULL;            /* pointer to the main process */
  56. int  HandlerActive = FALSE;             /* TRUE when handler has been added */
  57. LONG InputDevice = FALSE;               /* TRUE when Input.Device is open */
  58. LONG theSignal = 0;                     /* used when an event is freed */
  59. LONG theMask;                           /* 1 << theSignal */
  60.  
  61. UWORD Ticks = 0;                        /* number of ticks between events */
  62. LONG  TimerMics = 0;                    /* last timer event's micros field */
  63. LONG  TimerSecs = 0;                    /* last timer event's seconds field */
  64.  
  65. struct InputEvent *Event = NULL;        /* pointer to array of input events */
  66. struct SmallEvent TinyEvent;            /* a compressed event from the file */
  67. long   MaxEvents  = 50;                 /* size of the Event array */
  68. short  Smoothing  = TRUE;               /* TRUE if smoothing requested */
  69. short  LastPosted = 0;                  /* Event index for last-posted event */
  70. short  NextToPost = 0;                  /* Event index for next event to post */
  71. short  NextFree   = 0;                  /* Event index for next event to use */
  72.  
  73. FILE *InFile = NULL;                    /* journal file pointer */
  74. char *JournalFile = NULL;               /* name of journal file */
  75.  
  76.  
  77. struct Interrupt HandlerData =          /* used to add an input handler */
  78. {
  79.    {NULL, NULL, 0, 51, NULL},             /* Node structure (nl_Pri = 51) */
  80.    NULL,                                  /* data pointer */
  81.    &myHandlerStub                         /* code pointer */
  82. };
  83.  
  84.  
  85. /*
  86.  *  myHandler()
  87.  *
  88.  *  This is the input handler that posts the events read from the journal file.
  89.  *
  90.  *  First, free any events that were posted last time myHandler was
  91.  *  called by the Input.Device.  Signal the main process when any are freed,
  92.  *  in case it is waiting for an event to be freed.
  93.  *
  94.  *  Then, look through the list of events received from the Input.Device.
  95.  *  Check whether a new event is ready to be posted (i.e., one is available
  96.  *  and the proper number of ticks have been counted).  If so, then set its
  97.  *  time fields to the proper time, add it into the event list, and look at
  98.  *  the next event.  Set the tick count to zero again and check the next
  99.  *  event in the array.
  100.  *
  101.  *  Once any new events have been added, check whether the current event
  102.  *  from the Input.Device is a timer event.  If so, then increment the tick 
  103.  *  count and record its time field.  If not, then check whether it is a
  104.  *  raw mouse or raw key event.  If it is, then if it is a CTRL-C, signal the 
  105.  *  main process that the user wants to abort the playback.  Remove the mouse
  106.  *  or key event from the event list so that it will not interfere with the 
  107.  *  playback events (i.e., the keyboard and mouse are disabled while PLAYBACK
  108.  *  is running).
  109.  *
  110.  *  Finally, go on to the the next event in the chain and continue the loop.
  111.  *  Once all the events have been processed, return the modified list
  112.  *  (with new events added from the file and keyboard and mouse events removed)
  113.  *  so that Intuition can act on them.
  114.  */
  115.  
  116. struct InputEvent *myHandler(EventList,data)
  117. struct InputEvent *EventList;
  118. APTR data;
  119. {
  120.    struct InputEvent **EventPtr = &EventList;
  121.    struct InputEvent *toPost = &Event[NextToPost];
  122.    
  123.    while (NextToPost != LastPosted)
  124.    {
  125.       Event[LastPosted].my_InUse = FALSE;
  126.       Event[LastPosted].my_Ready = FALSE;
  127.       LastPosted = (LastPosted + 1) % MaxEvents;
  128.       Signal(theTask,theMask);
  129.    }
  130.    Forbid();
  131.    while (*EventPtr)
  132.    {
  133.       while (toPost->my_Ready && Ticks >= toPost->my_Ticks)
  134.       {
  135.          toPost->ie_Secs = TimerSecs;
  136.          toPost->ie_Mics += TimerMics;
  137.          if (toPost->ie_Mics > MILLION)
  138.          {
  139.             toPost->ie_Secs++;
  140.             toPost->ie_Mics -= MILLION;
  141.          }
  142.          toPost->ie_NextEvent = *EventPtr;
  143.          *EventPtr = toPost;
  144.          EventPtr = &(toPost->ie_NextEvent);
  145.          NextToPost = (NextToPost + 1) % MaxEvents;
  146.          toPost = &Event[NextToPost];
  147.          Ticks = 0;
  148.       }
  149.       if ((*EventPtr)->ie_Class == IECLASS_TIMER)
  150.       {
  151.          Ticks++;
  152.          TimerSecs = (*EventPtr)->ie_Secs;
  153.          TimerMics = (*EventPtr)->ie_Mics;
  154.       } else {
  155.          if ((*EventPtr)->ie_Class == IECLASS_RAWMOUSE ||
  156.              (*EventPtr)->ie_Class == IECLASS_RAWKEY)
  157.          {
  158.             if (CTRL_C(*EventPtr)) Signal(theTask,SIGBREAKF_CTRL_C);
  159.             *EventPtr = (*EventPtr)->ie_NextEvent;
  160.          }
  161.       }
  162.       EventPtr = &((*EventPtr)->ie_NextEvent);
  163.    }
  164.    Permit();
  165.    return(EventList);
  166. }
  167.  
  168.  
  169. /*
  170.  *  Ctrl_C()
  171.  *
  172.  *  Dummy routine to disable Lattice-C CTRL-C trapping.
  173.  */
  174.  
  175. #ifndef MANX
  176. int Ctrl_C()
  177. {
  178.    return(0);
  179. }
  180. #endif
  181.  
  182.  
  183. /*
  184.  *  DoExit()
  185.  *
  186.  *  General purpose exit routine.  If 's' is not NULL, then print an
  187.  *  error message with up to three parameters.  Remove the handler (if
  188.  *  it is active), free any memory, close any open files, delete any ports, 
  189.  *  free any used signals, etc.
  190.  */
  191.  
  192. void DoExit(s,x1,x2,x3)
  193. char *s, *x1, *x2, *x3;
  194. {
  195.    long status = 0;
  196.    
  197.    if (s != NULL)
  198.    {
  199.       printf(s,x1,x2,x3);
  200.       printf("\n");
  201.       status = RETURN_ERROR;
  202.    }
  203.    if (HandlerActive) RemoveHandler();
  204.    if (Event)         FreeMem(Event,IE_SIZE * MaxEvents);
  205.    if (InFile)        fclose(InFile);
  206.    if (InputDevice)   CloseDevice(InputBlock);
  207.    if (InputBlock)    DeleteStdIO(InputBlock);
  208.    if (InputPort)     DeletePort(InputPort);
  209.    if (theSignal)     FreeSignal(theSignal);
  210.    exit(status);
  211. }
  212.  
  213. /*
  214.  *  ParseArguements()
  215.  *
  216.  *  Check that all the command-line arguments are valid and set the 
  217.  *  proper variables as requested by the user.  If no keyword is specified,
  218.  *  assume "FROM".  If no file is specified, then show the usage.  EVENTS
  219.  *  regulates the size of the Event array used for buffering event 
  220.  *  communication between the main process and the handler.  SMOOTH and
  221.  *  NOSMOOTH regulate the interpolation of mouse movements between recorded
  222.  *  events.  The default is SMOOTH.
  223.  */
  224.  
  225. int ParseArguments(argc,argv)
  226. int argc;
  227. char **argv;
  228. {
  229.    int function = READ_JOURNAL;
  230.  
  231.    while (--argc > 0)
  232.    {
  233.       argv++;
  234.       if (argc > 1 && ARGMATCH("FROM"))
  235.       {
  236.          JournalFile = *(++argv);
  237.          argc--;
  238.       }
  239.       else if (argc > 1 && ARGMATCH("EVENTS"))
  240.       {
  241.          argc--;
  242.          if (sscanf(*(++argv),"%ld",&MaxEvents) != 1)
  243.          {
  244.             printf("Event count must be numeric:  '%s'\n",*argv);
  245.             function = JUST_EXIT;
  246.          }
  247.          if (MaxEvents <= 1)
  248.          {
  249.             printf("Event count must be greater than 1:  '%d'\n",MaxEvents);
  250.             function = JUST_EXIT;
  251.          }
  252.       }
  253.       else if (ARGMATCH("NOSMOOTH")) Smoothing = FALSE;
  254.       else if (ARGMATCH("SMOOTH"))   Smoothing = TRUE;
  255.       else if (JournalFile == NULL) JournalFile = *argv;
  256.       else function = SHOW_USAGE;
  257.    }
  258.    if (JournalFile == NULL && function == READ_JOURNAL) function = SHOW_USAGE;
  259.    return(function);
  260. }
  261.  
  262. /*
  263.  *  OpenJournal()
  264.  *
  265.  *  Open the journal file and check for errors.  Read the version
  266.  *  information to the file (someday we may need to check this).
  267.  */
  268.  
  269. void OpenJournal()
  270. {
  271.    char fileversion[32];
  272.  
  273.    InFile = fopen(JournalFile,"r");
  274.    if (InFile == NULL)
  275.       DoExit("Can't Open Journal File '%s', error %ld",JournalFile,_OSERR);
  276.    if (fread(fileversion,sizeof(fileversion),1,InFile) != 1)
  277.       DoExit("Can't read version from '%s', error %ld",JournalFile,_OSERR);
  278. }
  279.  
  280.  
  281. /*
  282.  *  GetEventMemory()
  283.  *
  284.  *  Allocate memory for the Event array (of size MaxEvents, specified by
  285.  *  the EVENT option).
  286.  */
  287.  
  288. void GetEventMemory()
  289. {
  290.    Event = AllocMem(IE_SIZE * MaxEvents, MEMF_CLEAR);
  291.    if (Event == NULL) DoExit("Can't get memory for %d Events",MaxEvents);
  292. }
  293.  
  294.  
  295. /*
  296.  *  GetSignal()
  297.  *
  298.  *  Allocate a signal (error if none available) and set the mask to
  299.  *  the proper value.
  300.  */
  301.  
  302. void GetSignal(theSignal,theMask)
  303. LONG *theSignal, *theMask;
  304. {
  305.    LONG signal;
  306.  
  307.    if ((signal = AllocSignal(-ONE)) == -ONE) DoExit("Can't Allocate Signal");
  308.    *theSignal = signal;
  309.    *theMask = (ONE << signal);
  310. }
  311.  
  312.  
  313. /*
  314.  *  SetupTask();
  315.  *
  316.  *  Find the task pointer for the main task (so the input handler can 
  317.  *  signal it).  Clear the CTRL signal flags (so we don't get any left
  318.  *  over from before PLAYBACK was run) and allocate a signal for 
  319.  *  when the handler frees an event.
  320.  */
  321.  
  322. void SetupTask()
  323. {
  324.    theTask = FindTask(NULL);
  325.    SetSignal(0L,SIGBREAKF_ANY);
  326.    GetSignal(&theSignal,&theMask);
  327.    #ifndef MANX
  328.       onbreak(&Ctrl_C);
  329.    #endif
  330. }
  331.  
  332.  
  333. /*
  334.  *  AddHandler()
  335.  *
  336.  *  Add the input handler to the Input.Device handler chain.  Since the
  337.  *  priority is 51 it will appear BEFORE intuition, so when we insert
  338.  *  new events into the chain, Intuition will process them just as though
  339.  *  they came from the Input.Device.
  340.  */
  341.  
  342. void AddHandler()
  343. {
  344.    long status;
  345.  
  346.    if ((InputPort = CreatePort(0,0)) == NULL)
  347.       DoExit("Can't Create Port");
  348.    if ((InputBlock = CreateStdIO(InputPort)) == NULL)
  349.       DoExit("Can't Create Standard IO Block");
  350.    InputDevice = (OpenDevice("input.device",0,InputBlock,0) == 0);
  351.    if (InputDevice == 0) DoExit("Can't Open Input.Device");
  352.    
  353.    InputBlock->io_Command = IND_ADDHANDLER;
  354.    InputBlock->io_Data    = (APTR) &HandlerData;
  355.    if (status = DoIO(InputBlock)) DoExit("Error from DoIO:  %ld",status);
  356.    printf("%s - Press CTRL-C to Cancel\n",version);
  357.    HandlerActive = TRUE;
  358. }
  359.  
  360. /*
  361.  *  RemoveHandler()
  362.  *
  363.  *  Remove the input handler from the Input.Device handler chain.
  364.  */
  365.  
  366. void RemoveHandler()
  367. {
  368.    long status;
  369.  
  370.    if (HandlerActive && InputDevice && InputBlock)
  371.    {
  372.       HandlerActive = FALSE;
  373.       InputBlock->io_Command = IND_REMHANDLER;
  374.       InputBlock->io_Data = (APTR) &HandlerData;
  375.       if (status = DoIO(InputBlock)) DoExit("Error from DoIO:  %ld",status);
  376.    }
  377.    printf("Playback Complete\n");
  378. }
  379.  
  380.  
  381. /*
  382.  *  Create an event that moves the pointer to the upper, left-hand corner
  383.  *  of the screen so that the pointer is at a known position.  This is a
  384.  *  large relative move (-1000,-1000).
  385.  */
  386.  
  387. void PointerToHome()
  388. {
  389.    struct InputEvent *theEvent = &(Event[0]);
  390.    
  391.    theEvent->ie_Class     = IECLASS_RAWMOUSE;
  392.    theEvent->ie_Code      = IECODE_NOBUTTON;
  393.    theEvent->ie_Qualifier = IEQUALIFIER_RELATIVEMOUSE;
  394.    theEvent->ie_X         = -1000;
  395.    theEvent->ie_Y         = -1000;
  396.    theEvent->my_Ticks     = 0;
  397.    theEvent->my_Time      = 0;
  398.    theEvent->my_Ready     = READY;
  399. }
  400.  
  401.  
  402. /*
  403.  *  CheckForCTRLC()
  404.  *
  405.  *  Read the current task signals (without changing them) and check whether
  406.  *  a CTRL-C has been signalled.  If so, abort the playback.
  407.  */
  408.  
  409. void CheckForCTRLC()
  410. {
  411.    LONG signals = SetSignal(0,0);
  412.    
  413.    if (signals & SIGBREAKF_CTRL_C) DoExit("Playback Aborted");
  414. }
  415.  
  416.  
  417. /*
  418.  *  GetNextFree()
  419.  *
  420.  *  Set NextFree to point to the next free event in the Event array.
  421.  *  If there are no free events, Wait() for the handler to signal that it
  422.  *  has freed one (or for CTRL-C to be pressed).
  423.  */
  424.  
  425. void GetNextFree()
  426. {
  427.    LONG signals;
  428.  
  429.    NextFree = (NextFree + 1) % MaxEvents;
  430.    while (Event[NextFree].my_InUse)
  431.    {
  432.       signals = Wait(theMask | SIGBREAKF_CTRL_C);
  433.       if (signals & SIGBREAKF_CTRL_C) DoExit("Playback Aborted");
  434.    }
  435. }
  436.  
  437.  
  438. #define ABS(x)  (((x)<0)?-(x):(x))
  439.  
  440.  
  441. /*
  442.  *  MovePointer()
  443.  *
  444.  *  Interpolate mouse move events that were compressed into one record in
  445.  *  the journal file.  The se_Count field holds the number of events that
  446.  *  were compressed into one.
  447.  *
  448.  *  First, unpack the X and Y movements.  Record their directions in dx and dy
  449.  *  and their magnitudes in abs_x and abs_y.  Reduce 'count' if there would
  450.  *  be events with offset of (0,0).  'x_move' specifies the x-offset for each
  451.  *  event and 'x_add' specifies the fraction of a pixel correction that must
  452.  *  be made (x_add/count is the fraction).  'x_count' counts the fraction of
  453.  *  a pixel that has been added so far (when x_count/count >= 1 (i.e., when
  454.  *  x_count >= count) we add another pixel to the x-offset).  Similarly for
  455.  *  the y and t variables (t is for ticks).  Starting the counts at 'count/2' 
  456.  *  makes for smoother movement.
  457.  *
  458.  *  Once these are set up, we create new mouse move events with the proper
  459.  *  offsets, adding up the fractions of pixels and adding in addional 
  460.  *  movements whenever the fractions add up to a whole pixel (or tick).
  461.  *  When a new event is set up, we mark it as READY so that the handler will
  462.  *  see it and post it.
  463.  *
  464.  *  Once we have sent all the events, the mouse should be in the proper
  465.  *  position, so we set the tick count and XY-offset fields to 0.
  466.  */
  467.  
  468. void MovePointer()
  469. {
  470.    WORD abs_x,abs_y, x_count,y_count, dx,dy, x_add,y_add, x_move,y_move;
  471.    WORD t_count, t_add, t_move;
  472.    WORD x = TinyEvent.se_XY & 0xFFF;
  473.    WORD y = (TinyEvent.se_XY >> 12) & 0xFFF;
  474.    WORD i, count = TinyEvent.se_Count & COUNTMASK;
  475.    LONG Time = TinyEvent.se_Micros & 0xFFFFF;
  476.    LONG Ticks = TinyEvent.se_Ticks >> 20;
  477.    struct InputEvent *NewEvent;
  478.  
  479.    x_count = y_count = t_count = 0;
  480.    if (x & 0x800) x |= 0xF000;
  481.    if (x < 0) dx = -1; else dx = 1;
  482.    if (y & 0x800) y |= 0xF000;
  483.    if (y < 0) dy = -1; else dy = 1;
  484.    abs_x = ABS(x); abs_y = ABS(y);
  485.    if (abs_x > abs_y)
  486.    {
  487.       if (count > abs_x) count = abs_x;
  488.    } else {
  489.       if (count > abs_y) count = abs_y;
  490.    }
  491.    if (count)
  492.    {
  493.       x_move = x / count; y_move = y / count; t_move = Ticks / count;
  494.       x_add = abs_x % count; y_add = abs_y % count; t_add = Ticks % count;
  495.    } else {
  496.       x_move = x; y_move = y; t_move = Ticks;
  497.       x_add = y_add = t_add = -1; count = 1;
  498.    }
  499.    x_count = y_count = t_count = count / 2;
  500.    for (i = count; i > 0; i--)
  501.    {
  502.       GetNextFree();
  503.       NewEvent = &Event[NextFree];
  504.       NewEvent->ie_Class     = IECLASS_RAWMOUSE;
  505.       NewEvent->ie_Code      = IECODE_NOBUTTON;
  506.       NewEvent->ie_Qualifier = TinyEvent.se_Qualifier;
  507.       NewEvent->ie_X         = x_move;
  508.       NewEvent->ie_Y         = y_move;
  509.       NewEvent->my_Ticks     = t_move;
  510.       NewEvent->my_Time      = Time;
  511.       if ((x_count += x_add) >= count)
  512.       {
  513.          x_count -= count;
  514.          NewEvent->ie_X += dx;
  515.       }
  516.       if ((y_count += y_add) >= count)
  517.       {
  518.          y_count -= count;
  519.          NewEvent->ie_Y += dy;
  520.       }
  521.       if ((t_count += t_add) > count)
  522.       {
  523.          t_count -= count;
  524.          NewEvent->my_Ticks++;
  525.       }
  526.       NewEvent->my_Ready = READY;
  527.    }
  528.    TinyEvent.se_XY    &= 0xFF000000;
  529.    TinyEvent.se_Ticks &= 0xFFFFF;
  530. }
  531.  
  532.  
  533. /*
  534.  *  PostNextEvent()
  535.  *
  536.  *  Read an event from the journal file.  If we are smoothing and the 
  537.  *  event is a mouse movement, them interpolate the compressed mouse
  538.  *  movements.  Get the next event in the Event array and unpack the
  539.  *  proper values from the TinyEvent read from the file.  Mark the finished
  540.  *  event as READY so the handler will see it and post it.
  541.  */
  542.  
  543. void PostNextEvent()
  544. {
  545.    struct InputEvent *NewEvent = NULL;
  546.  
  547.    if (fread((char *)&TinyEvent,sizeof(TinyEvent),1,InFile) == 1)
  548.    {
  549.       if (Smoothing && TinyEvent.se_Type == MOUSEMOVE) MovePointer();
  550.  
  551.       GetNextFree();
  552.       NewEvent = &Event[NextFree];
  553.    
  554.       NewEvent->ie_Class     = TinyEvent.se_Type & 0x1F;
  555.       NewEvent->ie_Qualifier = TinyEvent.se_Qualifier;
  556.       NewEvent->my_Ticks     = TinyEvent.se_Ticks >> 20;
  557.       NewEvent->my_Time      = TinyEvent.se_Micros & 0xFFFFF;
  558.       
  559.       switch(NewEvent->ie_Class)
  560.       {
  561.          case IECLASS_RAWKEY:
  562.             NewEvent->ie_Code = TinyEvent.se_Code;
  563.             NewEvent->ie_X = NewEvent->ie_Y = TinyEvent.se_Prev;
  564.             break;
  565.  
  566.          case IECLASS_RAWMOUSE:
  567.             NewEvent->ie_Code = (TinyEvent.se_Type >> 5) & 0x03;
  568.             if (NewEvent->ie_Code == 0x03)
  569.                NewEvent->ie_Code = IECODE_NOBUTTON;
  570.               else
  571.                NewEvent->ie_Code |= (TinyEvent.se_Type & IECODE_UP_PREFIX) |
  572.                   (IECODE_LBUTTON & ~(0x03 | IECODE_UP_PREFIX));
  573.             NewEvent->ie_X = TinyEvent.se_XY & 0xFFF;
  574.             NewEvent->ie_Y = (TinyEvent.se_XY >> 12) & 0xFFF;
  575.             NewEvent->my_Ticks = TinyEvent.se_Ticks >> 20;
  576.             if (NewEvent->ie_X & 0x800) NewEvent->ie_X |= 0xF000;
  577.             if (NewEvent->ie_Y & 0x800) NewEvent->ie_Y |= 0xF000;
  578.             break;
  579.  
  580.          default:
  581.             printf("[ Unknown Event Class:  %02X]\n",NewEvent->ie_Class);
  582.             break;
  583.       }
  584.       NewEvent->my_Ready = READY;
  585.    }
  586. }
  587.  
  588.  
  589. /*
  590.  *  WaitForEvents()
  591.  *
  592.  *  Wait for the handler to finish posting all the events in the Event
  593.  *  array (so we don't remove the handler before it is done).
  594.  */
  595.  
  596. void WaitForEvents()
  597. {
  598.    short LastFree = NextFree;
  599.    
  600.    do GetNextFree(); while (NextFree != LastFree);
  601. }
  602.  
  603.  
  604. /*
  605.  *  PlayJournal()
  606.  *
  607.  *  Open the journal file, set up the task and signals, and allocate the
  608.  *  Event array.  Add the input handler and send the pointer to the upper,
  609.  *  left-hand corner of the screen.  While there are still events in the
  610.  *  journal file, check whether the user wants to cancel the playback and
  611.  *  if not, post the next event in the file.  When the end-of-file is reached
  612.  *  wait for the handler to finish posting all the events in the array, and 
  613.  *  then remove the handler.
  614.  */
  615.  
  616. void PlayJournal()
  617. {
  618.    OpenJournal();
  619.    SetupTask();
  620.    GetEventMemory();
  621.  
  622.    AddHandler(&myHandler);
  623.    PointerToHome();
  624.  
  625.    while (feof(InFile) == 0)
  626.    {
  627.       CheckForCTRLC();
  628.       PostNextEvent();
  629.    }
  630.    
  631.    WaitForEvents();
  632.    RemoveHandler();
  633. }
  634.  
  635.  
  636. /*
  637.  *  main()
  638.  *
  639.  *  Parse the command-line arguments, and perform the proper function
  640.  *  (either show the usage, read a journal, or fall through and exit).
  641.  */
  642.  
  643. void main(argc,argv)
  644. int argc;
  645. char **argv;
  646. {
  647.    switch(ParseArguments(argc,argv))
  648.    {
  649.       case SHOW_USAGE:
  650.          printf("Usage:  %s\n",USAGE);
  651.          break;
  652.  
  653.       case READ_JOURNAL:
  654.          PlayJournal();
  655.          break;
  656.    }
  657.    DoExit(NULL);
  658. }
  659.